#include "sdk.h"
#include "ccmanager.h"
#include <wx/dirdlg.h>
#include <wx/filedlg.h>
#include <wx/html/htmlwin.h>
#include <wx/progdlg.h>
#include <wx/settings.h>
#include <wx/tooltip.h>
#include "pluginsconfigurationdlg.h"

inline int wxCALLBACK sortByTitle( long item1, long item2, M_Code_unused long sortData ) {
  const PluginElement* elem1 = ( const PluginElement* )item1;
  const PluginElement* elem2 = ( const PluginElement* )item2;
  return elem1->info.title.CompareTo( elem2->info.title );
}

BEGIN_EVENT_TABLE( PluginsConfigurationDlg, wxScrollingDialog )
  EVT_BUTTON( XRCID( "btnEnable" ), PluginsConfigurationDlg::OnToggle )
  EVT_BUTTON( XRCID( "btnDisable" ), PluginsConfigurationDlg::OnToggle )
  EVT_BUTTON( XRCID( "btnInstall" ), PluginsConfigurationDlg::OnInstall )
  EVT_BUTTON( XRCID( "btnUninstall" ), PluginsConfigurationDlg::OnUninstall )
  EVT_BUTTON( XRCID( "btnExport" ), PluginsConfigurationDlg::OnExport )
  EVT_UPDATE_UI( -1, PluginsConfigurationDlg::OnUpdateUI )
END_EVENT_TABLE()

PluginsConfigurationDlg::PluginsConfigurationDlg( wxWindow* parent ) {
  wxXmlResource* xml_res = wxXmlResource::Get();
  wxString resPath = ConfigManager::GetDataFolder() + _T( "/配置插件.xrc" );
  if( !xml_res->Load( resPath ) )  {
    xml_File_lock = false;
    Manager::Get()->GetLogManager()->LogInf( F( _T( "文件丢失: %s " ), resPath.wx_str() ) );
    return;
  }
  if( !xml_res->LoadObject( this, parent, _T( "dlgConfigurePlugins" ), _T( "wxScrollingDialog" ) ) ) {
    xml_File_lock = false;
    Manager::Get()->GetLogManager()->LogInf( _T( "资源丢失" ) );
    return;
  }
  xml_File_lock = true;
  XRCCTRL( *this, "wxID_CANCEL", wxButton )->SetDefault();
  FillList();
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "plugins" ) );
  bool globalInstall = cfg->ReadBool( _T( "/install_globally" ), true );
  bool confirmation = cfg->ReadBool( _T( "/install_confirmation" ), true );
  DirAccessCheck access = cbDirAccessCheck( ConfigManager::GetFolder( sdPluginsGlobal ) );
  if( access != dacReadWrite ) {
    globalInstall = false;
    XRCCTRL( *this, "chkInstallGlobally", wxCheckBox )->Enable( false );
  }
  XRCCTRL( *this, "chkInstallGlobally", wxCheckBox )->SetValue( globalInstall );
  XRCCTRL( *this, "chkInstallConfirmation", wxCheckBox )->SetValue( confirmation );
  XRCCTRL( *this, "lstPlugins", wxListCtrl )->
  Connect( wxEVT_LEAVE_WINDOW, wxMouseEventHandler( PluginsConfigurationDlg::OnMouseMotion ) );
  XRCCTRL( *this, "lstPlugins", wxListCtrl )->
  Connect( wxEVT_MOTION, wxMouseEventHandler( PluginsConfigurationDlg::OnMouseMotion ) );
}

void PluginsConfigurationDlg::FillList() {
  wxListCtrl* list = XRCCTRL( *this, "lstPlugins", wxListCtrl );
  if( list->GetColumnCount() == 0 ) {
    list->InsertColumn( 0, _T( "Title" ) );
    list->InsertColumn( 1, _T( "Enabled" ), wxLIST_FORMAT_CENTER );
    list->InsertColumn( 2, _T( "Filename" ) );
  }
  PluginManager* man = Manager::Get()->GetPluginManager();
  const PluginElementsArray& plugins = man->GetPlugins();
  list->DeleteAllItems();
  for( unsigned int i = 0; i < plugins.GetCount(); ++i ) {
    const PluginElement* elem = plugins[i];
    long idx = list->InsertItem( i, elem->info.title );
    list->SetItem( idx, 1, elem->plugin->IsAttached() ? _T( " O " ) : _T( " X " ) );
    list->SetItem( idx, 2, UnixFilename( elem->fileName ).AfterLast( wxFILE_SEP_PATH ) );
    list->SetItemData( idx, ( wxIntPtr )elem );
    if( !elem->plugin->IsAttached() ) {
      list->SetItemTextColour( idx, wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
    } else
    { list->SetItemTextColour( idx, wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) ); }
  }
  list->SetColumnWidth( 0, wxLIST_AUTOSIZE );
  list->SetColumnWidth( 1, wxLIST_AUTOSIZE_USEHEADER );
  list->SetColumnWidth( 2, wxLIST_AUTOSIZE );
  list->SortItems( sortByTitle, 0 );
}


PluginsConfigurationDlg::~PluginsConfigurationDlg() {
  XRCCTRL( *this, "lstPlugins", wxListCtrl )->
  Disconnect( wxEVT_LEAVE_WINDOW, wxMouseEventHandler( PluginsConfigurationDlg::OnMouseMotion ) );
  XRCCTRL( *this, "lstPlugins", wxListCtrl )->
  Disconnect( wxEVT_MOTION, wxMouseEventHandler( PluginsConfigurationDlg::OnMouseMotion ) );
}

void PluginsConfigurationDlg::OnToggle( wxCommandEvent& event ) {
  wxListCtrl* list = XRCCTRL( *this, "lstPlugins", wxListCtrl );
  if( list->GetSelectedItemCount() == 0 ) {
    return;
  }
  bool isEnable = event.GetId() == XRCID( "btnEnable" );
  wxBusyCursor busy;
  wxProgressDialog pd( F( _T( "%s plugin(s)" ), isEnable ? _T( "启用" ) : _T( "禁用" ) ), _T( "对对话框足够宽的描述 ;)" ),
                       list->GetSelectedItemCount(), this, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT );
  int count = 0;
  long sel = -1;
  bool skip = false;
  while( true ) {
    sel = list->GetNextItem( sel, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
    if( sel == -1 ) {
      break;
    }
    const PluginElement* elem = ( const PluginElement* )list->GetItemData( sel );
    if( elem && elem->plugin ) {
      pd.Update( ++count, F( _T( "%s \"%s\"..." ), isEnable ? _T( "启用" ) : _T( "禁用" ), elem->info.title.c_str() ), &skip );
      if( skip ) {
        break;
      }
      if( !isEnable && elem->plugin->IsAttached() ) {
        Manager::Get()->GetPluginManager()->DetachPlugin( elem->plugin );
      } else if( isEnable && !elem->plugin->IsAttached() ) {
        Manager::Get()->GetPluginManager()->AttachPlugin( elem->plugin, true );
      } else
      { continue; }
      wxListItem item;
      item.SetId( sel );
      item.SetColumn( 2 );
      item.SetMask( wxLIST_MASK_TEXT );
      list->GetItem( item );
      list->SetItem( sel, 2, elem->plugin->IsAttached() ? _T( "Yes" ) : _T( "No" ) );
      if( !elem->plugin->IsAttached() ) {
        list->SetItemTextColour( sel, wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
      } else
      { list->SetItemTextColour( sel, wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) ); }
      wxString baseKey;
      baseKey << _T( "/" ) << elem->info.name;
      Manager::Get()->GetConfigManager( _T( "plugins" ) )->Write( baseKey, elem->plugin->IsAttached() );
    }
  }
}

void PluginsConfigurationDlg::OnInstall( M_Code_unused wxCommandEvent& event ) {
  wxFileDialog fd( this, _T( "选择要安装的插件" ), wxEmptyString, wxEmptyString, _T( "插件包 (*m_plugin)|*.m_plugin" ),
                   wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE | compatibility::wxHideReadonly );
  if( fd.ShowModal() != wxID_OK ) {
    return;
  }
  wxBusyCursor busy;
  wxArrayString paths;
  fd.GetPaths( paths );
  bool globalInstall = XRCCTRL( *this, "chkInstallGlobally", wxCheckBox )->GetValue();
  bool confirm = XRCCTRL( *this, "chkInstallConfirmation", wxCheckBox )->GetValue();
  wxString failure;
  for( size_t i = 0; i < paths.GetCount(); ++i ) {
    if( !Manager::Get()->GetPluginManager()->InstallPlugin( paths[i], globalInstall, confirm ) ) {
      failure << paths[i] << _T( '\n' );
    }
  }
  FillList();
  if( !failure.IsEmpty() ) {
    msging( _T( "插件安装失败:\n\n" ) + failure, _T( "警告" ), wxICON_WARNING, this );
  }
}

void PluginsConfigurationDlg::OnUninstall( M_Code_unused wxCommandEvent& event ) {
  wxListCtrl* list = XRCCTRL( *this, "lstPlugins", wxListCtrl );
  if( list->GetSelectedItemCount() == 0 ) {
    return;
  }
  wxBusyCursor busy;
  long sel = -1;
  wxString failure;
  while( true ) {
    sel = list->GetNextItem( sel, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
    if( sel == -1 ) {
      break;
    }
    const PluginElement* elem = ( const PluginElement* )list->GetItemData( sel );
    if( elem && elem->plugin ) {
      if( !Manager::Get()->GetPluginManager()->UninstallPlugin( elem->plugin ) ) {
        failure << elem->info.title << _T( '\n' );
      }
    }
  }
  FillList();
  if( !failure.IsEmpty() ) {
    msging( _T( "插件未能成功卸载:\n\n" ) + failure, _T( "警告" ), wxICON_WARNING, this );
  }
}

void PluginsConfigurationDlg::OnExport( M_Code_unused wxCommandEvent& event ) {
  wxListCtrl* list = XRCCTRL( *this, "lstPlugins", wxListCtrl );
  if( list->GetSelectedItemCount() == 0 ) {
    return;
  }
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "plugins_configuration" ) );
  wxDirDialog dd( this, _T( "选择要导出插件的目录" ), cfg->Read( _T( "/last_export_path" ) ), wxDD_NEW_DIR_BUTTON );
  if( dd.ShowModal() != wxID_OK ) {
    return;
  }
  cfg->Write( _T( "/last_export_path" ), dd.GetPath() );
  wxBusyCursor busy;
  wxProgressDialog pd( _T( "导出插件" ), _T( "对对话框足够宽的描述 ;)" ), list->GetSelectedItemCount(), this,
                       wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME );
  int count = 0;
  long sel = -1;
  bool skip = false;
  bool confirmed = false;
  wxString failure;
  wxArrayString files;
  while( true ) {
    sel = list->GetNextItem( sel, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
    if( sel == -1 ) {
      break;
    }
    const PluginElement* elem = ( const PluginElement* )list->GetItemData( sel );
    if( !elem && !elem->plugin ) {
      failure << list->GetItemText( sel ) << _T( '\n' );
      continue;
    }
    if( files.Index( elem->fileName ) != wxNOT_FOUND ) {
      continue;
    }
    files.Add( elem->fileName );
    wxFileName fname;
    fname.SetPath( dd.GetPath() );
    fname.SetName( wxFileName( elem->fileName ).GetName() );
    fname.SetExt( _T( "cbplugin" ) );
    pd.Update( ++count, F( _T( "导出 \"%s\"..." ), elem->info.title.c_str() ), &skip );
    if( skip ) {
      break;
    }
    wxString filename = fname.GetFullPath();
    if( !confirmed && wxFileExists( filename ) ) {
      InfoWindow::Tip( F( _T( "%s 文件遭到覆盖" ), filename.c_str() ), _T( "提示" ), 3500 );
      confirmed = true;
    }
    if( !Manager::Get()->GetPluginManager()->ExportPlugin( elem->plugin, filename ) ) {
      failure << list->GetItemText( sel ) << _T( '\n' );
    }
  }
  if( !failure.IsEmpty() ) {
    InfoWindow::Tip( _T( "无法导出插件:\n\n" ) + failure, _T( "错误" ), 3500 );
  }
}

void PluginsConfigurationDlg::OnMouseMotion( wxMouseEvent& event ) {
  event.Skip();
  wxListCtrl* list = XRCCTRL( *this, "lstPlugins", wxListCtrl );
  if( event.Leaving() ) {
    if( list->GetToolTip() ) {
      list->UnsetToolTip();
    }
    return;
  }
  int flags = 0;
  long idx = list->HitTest( event.GetPosition(), flags );
  wxString path;
  if( flags & wxLIST_HITTEST_ONITEM ) {
    const PluginElement* elem = ( const PluginElement* )list->GetItemData( idx );
    if( elem ) {
      path = elem->fileName;
    }
  }
  if( list->GetToolTip() ) {
    if( path.IsEmpty() ) {
      list->UnsetToolTip();
    } else if( path != list->GetToolTip()->GetTip() ) {
      list->SetToolTip( path );
    }
  } else if( !path.IsEmpty() ) {
    list->SetToolTip( path );
  }
}

void PluginsConfigurationDlg::OnUpdateUI( wxUpdateUIEvent& event ) {
  static long lastSelection = -2;
  static bool lastSelectionMultiple = false;
  event.Skip();
  wxListCtrl* list = XRCCTRL( *this, "lstPlugins", wxListCtrl );
  long sel = list->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
  if( sel == lastSelection && ( lastSelectionMultiple && list->GetSelectedItemCount() > 1 ) ) {
    return;
  }
  lastSelection = sel;
  lastSelectionMultiple = list->GetSelectedItemCount() > 1;
  bool en = sel != -1;
  const PluginElement* elem = en ? ( const PluginElement* )list->GetItemData( sel ) : nullptr;
  bool hasPlugin = elem && elem->plugin;
  bool isAttached = hasPlugin && elem->plugin->IsAttached();
  XRCCTRL( *this, "btnEnable", wxButton )->Enable( en && ( lastSelectionMultiple || ( hasPlugin && !isAttached ) ) );
  XRCCTRL( *this, "btnDisable", wxButton )->Enable( en && ( lastSelectionMultiple || ( hasPlugin && isAttached ) ) );
  XRCCTRL( *this, "btnUninstall", wxButton )->Enable( en );
  XRCCTRL( *this, "btnExport", wxButton )->Enable( en );
}

void PluginsConfigurationDlg::EndModal( int retCode ) {
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "plugins" ) );
  cfg->Write( _T( "/install_globally" ), XRCCTRL( *this, "chkInstallGlobally", wxCheckBox )->GetValue() );
  cfg->Write( _T( "/install_confirmation" ), XRCCTRL( *this, "chkInstallConfirmation", wxCheckBox )->GetValue() );
  Manager::Get()->GetCCManager()->NotifyPluginStatus();
  wxScrollingDialog::EndModal( retCode );
}
